home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
ole2book.zip
/
CHAP09.ZIP
/
CHAP09
/
PATRON
/
TENANT.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-23
|
38KB
|
1,531 lines
/*
* TENANT.CPP
* Modifications for Chapter 9
*
* Implementation of the CTentant class which holds information
* for a single object on a page. It maintains position, references
* to data, and a storage.
*
* Copyright (c)1993 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Software Design Engineer
* Microsoft Systems Developer Relations
*
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/
#include "patron.h"
/*
* CTenant::CTenant
* CTenant::~CTenant
*
* Constructor Parameters:
* dwID DWORD identifier for this page.
* hWnd HWND of the pages window.
* pPG LPCPages to the parent structure.
*/
CTenant::CTenant(DWORD dwID, HWND hWnd, LPCPages pPG)
{
m_hWnd=hWnd;
m_dwID=dwID;
m_fInitialized=0;
m_pIStorage=NULL;
m_cOpens=0;
m_pObj=NULL;
m_pPG =pPG;
//CHAPTER9MOD
m_cRef=0;
m_pIOleObject=NULL;
m_pIViewObject=NULL;
m_pIOleClientSite=NULL;
m_pIAdviseSink=NULL;
//End CHAPTER9MOD
return;
}
CTenant::~CTenant(void)
{
//CHAPTER9MOD
if (NULL!=m_pIViewObject)
{
m_pIViewObject->SetAdvise(m_fe.dwAspect, 0, NULL);
m_pIViewObject->Release();
}
if (NULL!=m_pIOleObject)
m_pIOleObject->Release();
//We delete our own interfaces since we control them
if (NULL!=m_pIAdviseSink)
delete m_pIAdviseSink;
if (NULL!=m_pIOleClientSite)
delete m_pIOleClientSite;
//End CHAPTER9MOD
if (NULL!=m_pObj)
{
//We know we only hold one reference from UCreate or FLoad
m_pObj->Release();
m_pObj=NULL;
}
return;
}
//CHAPTER9MOD
/*
* CTenant::QueryInterface
* CTenant::AddRef
* CTenant::Release
*
* Purpose:
* IUnknown members for CTenant object.
*/
STDMETHODIMP CTenant::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
*ppv=NULL;
//Any interface on this object is the object pointer.
if (IsEqualIID(riid, IID_IUnknown))
*ppv=(LPVOID)this;
if (IsEqualIID(riid, IID_IOleClientSite))
*ppv=(LPVOID)m_pIOleClientSite;
if (IsEqualIID(riid, IID_IAdviseSink))
*ppv=(LPVOID)m_pIAdviseSink;
/*
* If we actually assign an interface to ppv we need to AddRef it
* since we're returning a new pointer.
*/
if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CTenant::AddRef(void)
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) CTenant::Release(void)
{
ULONG cRefT;
cRefT=--m_cRef;
if (0L==m_cRef)
delete this;
return cRefT;
}
//End CHAPTER9MOD
/*
* CTenant::GetID
*
* Return Value:
* DWORD dwID field in this tenant. This function is only here
* to avoid hiding inline implementations in pages.h
*/
DWORD CTenant::GetID(void)
{
return m_dwID;
}
/*
* CTenant::GetStorageName
*
* Parameters:
* pszName LPSTR to a buffer in which to store the storage name
* for this tenant.
*
* Return Value:
* UINT Number of characters stored.
*/
UINT CTenant::GetStorageName(LPSTR pszName)
{
return wsprintf(pszName, "Tenant %lu", m_dwID);
}
/*
* CTenant::UCreate
*
* Purpose:
* Creates a new tenant of the given CLSID, which can be either a
* static bitmap or metafile now (Chapter 7) and which may eventually
* be any OLE object.
*
* Parameters:
* tType TENANTTYPE to create, either a static metafile, bitmap,
* or some kind of OLE object (later chapters)
* This determined which OleCreate* call we use.
* pvType LPVOID providing the relevant pointer from which
* to create the tenant, depending on iType.
* pFE LPFORMATETC specifying the type of renderings to use.
* pptl LPPOINTL in which we can store offset coordinates.
* pszl LPSIZEL where this object should store its lometric extents.
* pIStorage LPSTORAGE of the page we live in. We have to
* create another storage under this for the tenant.
* ppo LPPATRONOBJECT containing placement data.
* dwData DWORD containing extra data, sensitive to iType.
*
* Return Value:
* UINT A UCREATE_* value depending on what we actually do.
*/
UINT CTenant::UCreate(TENANTTYPE tType, LPVOID pvType, LPFORMATETC pFE
, LPPOINTL pptl, LPSIZEL pszl, LPSTORAGE pIStorage
, LPPATRONOBJECT ppo, DWORD dwData)
{
HRESULT hr;
LPUNKNOWN pObj;
UINT uRet=UCREATE_GRAPHICONLY;
if (NULL==pvType || NULL==pIStorage)
return UCREATE_FAILED;
//Fail if this is called for an already living tenant.
if (m_fInitialized)
return UCREATE_FAILED;
m_fInitialized=TRUE;
//Create a new storage for this tenant.
if (!FOpen(pIStorage))
return UCREATE_FAILED;
/*
* Get the placement info if it's here. We either have a non-NULL
* LPPATRONOBJECT in ppo or we have to use default placement and
* retrieve the size from the object itself.
*/
pszl->cx=0;
pszl->cy=0;
if (NULL!=ppo)
{
*pFE=ppo->fe;
*pptl=ppo->ptl;
*pszl=ppo->szl; //Could be 0,0 in which case we ask object
uRet=UCREATE_PLACEDOBJECT;
}
hr=ResultFromScode(E_FAIL);
//Now create an object based specifically for the type.
switch (tType)
{
case TENANTTYPE_NULL:
break;
case TENANTTYPE_STATIC:
/*
* We could use OleCreateStaticFromData here which does
* pretty much what we're doing below. However, it does
* not allow us to control whether we paste a bitmap or
* a metafile--it uses metafile first, bitmap second. For
* this reason we'll use code developed in Chapter 6's
* FreeLoader to affect the paste.
*/
hr=CreateStatic((LPDATAOBJECT)pvType, pFE, &pObj);
break;
//CHAPTER9MOD
case TENANTTYPE_EMBEDDEDOBJECT:
hr=OleCreate(*((LPCLSID)pvType), IID_IUnknown, OLERENDER_DRAW
, NULL, NULL, m_pIStorage, (LPLPVOID)&pObj);
break;
case TENANTTYPE_EMBEDDEDFILE:
hr=OleCreateFromFile(CLSID_NULL, (LPSTR)pvType, IID_IUnknown
, OLERENDER_DRAW, NULL, NULL, m_pIStorage, (LPLPVOID)&pObj);
break;
case TENANTTYPE_EMBEDDEDOBJECTFROMDATA:
hr=OleCreateFromData((LPDATAOBJECT)pvType, IID_IUnknown
, OLERENDER_DRAW, NULL, NULL, m_pIStorage, (LPLPVOID)&pObj);
break;
//End CHAPTER9MOD
default:
break;
}
//If creation didn't work, get rid for the element FOpen created.
if (FAILED(hr))
{
Destroy(pIStorage);
return UCREATE_FAILED;
}
//CHAPTER9MOD
//We don't get the size if PatronObject data was seen already.
FObjectInitialize(pObj, pFE, dwData);
//We depend here on m_pIOleObject having been initialized.
if ((0==pszl->cx && 0==pszl->cy))
{
SIZEL szl;
//Try to get the real size of the object, default to 2"*2"
SETSIZEL((*pszl), 2*LOMETRIC_PER_INCH, 2*LOMETRIC_PER_INCH);
if (SUCCEEDED(m_pIOleObject->GetExtent(pFE->dwAspect, &szl)))
{
/*
* Protect ourselves from OLE 1.0 servers that did MM_HIMETRIC
* instead of HIMETRIC units, thus had a negative y extent.
*/
if (0 > szl.cy)
szl.cy=-szl.cy;
//Convert HIMETRIC to our LOMETRIC mapping, if meaningful
if (0!=szl.cx && 0!=szl.cy)
SETSIZEL((*pszl), szl.cx/10, szl.cy/10);
}
}
//End CHAPTER9MOD
return uRet;
}
/*
* CTenant::FLoad
*
* Purpose:
* Recreates the object living in this tenant in place of calling
* FCreate. This is used in loading as opposed to new creation.
*
* Parameters:
* pIStorage LPSTORAGE of the page we live in.
* pFE LPFORMATETC specifying the type of renderings to use.
* prcl LPRECTL where this object is positioned.
*
* Return Value:
* BOOL TRUE if successful, FALSE otherwise.
*/
BOOL CTenant::FLoad(LPSTORAGE pIStorage, LPFORMATETC pFE, LPRECTL prcl)
{
HRESULT hr;
LPUNKNOWN pObj;
if (NULL==pIStorage || NULL==pFE || NULL==prcl)
return FALSE;
//Fail if this is called for an already living tenant.
if (m_fInitialized)
return FALSE;
m_fInitialized=TRUE;
//Open the storage for this tenant.
if (!FOpen(pIStorage))
return FALSE;
hr=OleLoad(m_pIStorage, IID_IUnknown, NULL, (LPVOID FAR *)&pObj);
if (FAILED(hr))
{
Destroy(pIStorage);
return FALSE;
}
//CHAPTER9MOD
FObjectInitialize(pObj, pFE, NULL);
//End CHAPTER9MOD
RectSet(prcl, FALSE);
return TRUE;
}
//CHAPTER9MOD
/*
* CTenant::FObjectInitialize
* (Protected)
*
* Purpose:
* Performs operations necessary after creating an object or reloading
* one from storage.
*
* Parameters:
* pObj LPUNKNOWN of the object in this tenant.
* pFE LPFORMATETC describing the graphic here.
* dwData DWORD extra data. If pFE->dwAspect==DVASPECT_ICON
* then this is the iconic metafile.
*
* Return Value:
* BOOL TRUE if the function succeeded, FALSE otherwise.
*/
BOOL CTenant::FObjectInitialize(LPUNKNOWN pObj, LPFORMATETC pFE, DWORD dwData)
{
HRESULT hr;
LPPERSIST pIPersist=NULL;
DWORD dw;
LPCDocument pDoc;
char szFile[CCHPATHMAX];
if (NULL==pObj || NULL==pFE)
return FALSE;
m_pObj=pObj;
m_fe=*pFE;
m_dwState=TENANTSTATE_DEFAULT;
/*
* Determine the type: Static or Embedded
* If Static, this will have CLSID_FreeMetafile or CLSID_FreeDib.
* Otherwise it's embedded. Later we'll add a case for links.
*/
m_tType=TENANTTYPE_EMBEDDEDOBJECT;
if (SUCCEEDED(pObj->QueryInterface(IID_IPersist, (LPLPVOID)&pIPersist)))
{
CLSID clsid;
pIPersist->GetClassID(&clsid);
if (IsEqualCLSID(clsid, CLSID_FreeMetafile)
|| IsEqualCLSID(clsid, CLSID_FreeDib))
m_tType=TENANTTYPE_STATIC;
pIPersist->Release();
}
m_pIViewObject=NULL;
hr=pObj->QueryInterface(IID_IViewObject, (LPLPVOID)&m_pIViewObject);
if (FAILED(hr))
return FALSE;
m_pIViewObject->SetAdvise(pFE->dwAspect, 0, m_pIAdviseSink);
//We need an IOleObject most of the time, so get one here.
m_pIOleObject=NULL;
hr=pObj->QueryInterface(IID_IOleObject, (LPLPVOID)&m_pIOleObject);
//Follow up object creation with advises and so forth.
if (FAILED(hr))
return FALSE;
/*
* We could pass m_pIOleClientSite in an OleCreate* call, but
* since this function could be called after OleLoad, we still
* need to do this here, so it's always done here...
*/
m_pIOleObject->SetClientSite(m_pIOleClientSite);
m_pIOleObject->Advise(m_pIAdviseSink, &dw);
OleSetContainedObject((LPUNKNOWN)m_pIOleObject, TRUE);
/*
* For IOleObject::SetHostNames we need the application name
* and the document name (which is passed in the object parameter).
* The design of Patron doesn't give us nice structured access to
* the name of the document we're in, so I grab the parent of
* the Pages window (the document) and send it DOCM_PDOCUMENT
* which returns us the pointer. Roundabout, but it works.
*/
pDoc=(LPCDocument)SendMessage(GetParent(m_hWnd), DOCM_PDOCUMENT, 0, 0L);
if (NULL!=pDoc)
pDoc->FilenameGet(szFile, CCHPATHMAX);
else
szFile[0]=0;
NotifyOfRename(szFile, NULL);
/*
* This might have been Display as Icon if pFE->dwAspect=DVASPECT_ICON.
* If so, then dwData is a handle to a metafile with the iconic
* aspect. We take this and shove it into the cache for this
* aspect, releasing the content aspect. OLE2UI has a nice function
* that does this: OleStdSwitchDisplayAspect, which also handles a
* later case (Chapter 14) when we might want to switch BACK to content.
* That, however, requires the Change Type dialog.
*/
if ((DVASPECT_ICON & pFE->dwAspect) && NULL!=dwData)
{
//Temps to give to OleStdSwitchDisplayAspect
DWORD dw=DVASPECT_CONTENT;
BOOL fUpdate;
OleStdSwitchDisplayAspect(m_pIOleObject, &dw, DVASPECT_ICON
, (HGLOBAL)(UINT)dwData, TRUE, FALSE, NULL, &fUpdate);
}
return TRUE;
}
//End CHAPTER9MOD
/*
* CTenant::FOpen
*
* Purpose:
* Retrieves the IStorage associated with this tenant. The IStorage is
* owned by the tenant and thus the tenant always holds a reference count.
*
* If the storage is already open for this tenant, then this function will
* AddRef it; therefore the caller must always match an FOpen with a Close.
*
* Parameters:
* pIStorage LPSTORAGE above this tenant (which has its own storage).
*
* Return Value:
* BOOL TRUE if opening succeeds, FALSE otherwise.
*/
BOOL CTenant::FOpen(LPSTORAGE pIStorage)
{
HRESULT hr=NOERROR;
DWORD dwMode=STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
char szTemp[32];
if (NULL==m_pIStorage)
{
if (NULL==pIStorage)
return FALSE;
/*
* Attempt to open the storage under this ID. If there is none, then
* create it. In either case we end up with an IStorage that we
* either save in pPage or release.
*/
GetStorageName(szTemp);
hr=pIStorage->OpenStorage(szTemp, NULL, dwMode, NULL, 0, &m_pIStorage);
if (FAILED(hr))
hr=pIStorage->CreateStorage(szTemp, dwMode, 0, 0, &m_pIStorage);
}
else
m_pIStorage->AddRef();
if (FAILED(hr))
return FALSE;
m_cOpens++;
//CHAPTER9MOD
m_pIOleClientSite=new CImpIOleClientSite(this, (LPUNKNOWN)this);
m_pIAdviseSink=new CImpIAdviseSink(this, (LPUNKNOWN)this);
if (NULL==m_pIOleClientSite || NULL==m_pIAdviseSink)
return FALSE;
//End CHAPTER9MOD
return TRUE;
}
/*
* CTenant::Close
*
* Purpose:
* Possibly commits the storage, then releases it reversing the
* reference count from FOpen. If the reference on the storage
* goes to zero, the storage is forgotten. However, the object we
* contain is still held and as long as it's active the storage
* remains alive.
*
* Parameters:
* fCommit BOOL indicating if we're to commit.
*
* Return Value:
* None
*/
void CTenant::Close(BOOL fCommit)
{
if (fCommit)
Update();
if (NULL!=m_pIStorage)
{
m_pIStorage->Release();
/*
* We can't use a zero reference count to know when to NULL
* this since other things might have AddRef'd the storage.
*/
//CHAPTER9MOD
if (0==--m_cOpens)
{
m_pIStorage=NULL;
//Close the object saving if necessary
if (NULL!=m_pIOleObject)
m_pIOleObject->Close(OLECLOSE_SAVEIFDIRTY);
}
//End CHAPTER9MOD
}
return;
}
/*
* CTenant::Update
*
* Purpose:
* Forces a common on the page if it's open.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if the object is open, FALSE otherwise.
*/
BOOL CTenant::Update(void)
{
LPPERSISTSTORAGE pIPS;
if (NULL!=m_pIStorage)
{
/*
* We need to OleSave again because we might have changed the
* size or position of this tenant. We also need to save the
* rectangle on the page, since that's not known to OLE.
*/
m_pObj->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPS);
OleSave(pIPS, m_pIStorage, TRUE);
pIPS->SaveCompleted(NULL);
pIPS->Release();
m_pIStorage->Commit(STGC_ONLYIFCURRENT);
}
return FALSE;
}
/*
* CTenant::Destroy
*
* Purpose:
* Removes this page from the given storage. The caller should
* eventually delete this CTenant object to free the object herein.
* Nothing is committed when being destroyed.
*
* Parameters:
* pIStorage LPSTORAGE contianing this page on which to call
* ::DestroyElement
*
* Return Value:
* None
*/
void CTenant::Destroy(LPSTORAGE pIStorage)
{
char szTemp[32];
if (NULL!=pIStorage)
{
//CHAPTER9MOD
if (NULL!=m_pIOleObject)
m_pIOleObject->Close(OLECLOSE_NOSAVE);
//End CHAPTER9MOD
if (NULL!=m_pIStorage)
{
//Remove all reference/open counts on this storage.
while (0!=m_cOpens)
{
m_pIStorage->Release();
m_cOpens--;
}
}
GetStorageName(szTemp);
pIStorage->DestroyElement(szTemp);
m_pIStorage=NULL;
}
//m_pObj is released in destructor.
return;
}
/*
* CTenant::Select
*
* Purpose:
* Selects or deselects the tenant.
*
* Parameters:
* fSelect BOOL indicating the new state of the tenant.
*
* Return Value:
* None
*/
void CTenant::Select(BOOL fSelect)
{
BOOL fWasSelected;
DWORD dwState;
RECT rc;
HDC hDC;
fWasSelected=(BOOL)(TENANTSTATE_SELECTED & m_dwState);
//Nothing to do when there's no change.
if (fWasSelected==fSelect)
return;
dwState=m_dwState & ~TENANTSTATE_SELECTED;
m_dwState=dwState | ((fSelect) ? TENANTSTATE_SELECTED : 0);
/*
* Draw sizing handles to show the selection state. We convert
* things to MM_TEXT since that's what this function expects.
*/
RECTFROMRECTL(rc, m_rcl);
RectConvertMappings(&rc, NULL, TRUE);
OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
hDC=GetDC(m_hWnd);
OleUIDrawHandles(&rc, hDC, OLEUI_HANDLES_INSIDE | OLEUI_HANDLES_NOBORDER
| OLEUI_HANDLES_USEINVERSE, CXYHANDLE, !fWasSelected);
ReleaseDC(m_hWnd, hDC);
return;
}
//CHAPTER9MOD
/*
* CTenant::ShowAsOpen
*
* Purpose:
* Draws or removes the hatch pattern over an object.
*
* Parameters:
* fOpen BOOL indicating the open state of this tenant.
*
* Return Value:
* None
*/
void CTenant::ShowAsOpen(BOOL fOpen)
{
BOOL fWasOpen;
DWORD dwState;
RECT rc;
HDC hDC;
fWasOpen=(BOOL)(TENANTSTATE_OPEN & m_dwState);
dwState=m_dwState & ~TENANTSTATE_OPEN;
m_dwState=dwState | ((fOpen) ? TENANTSTATE_OPEN : 0);
//If this was not open, then just hatch, otherwise repaint.
if (!fWasOpen && fOpen)
{
RECTFROMRECTL(rc, m_rcl);
RectConvertMappings(&rc, NULL, TRUE);
OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
hDC=GetDC(m_hWnd);
OleUIDrawShading(&rc, hDC, OLEUI_SHADE_FULLRECT, 0);
ReleaseDC(m_hWnd, hDC);
}
if (fWasOpen && !fOpen)
Repaint();
return;
}
/*
* CTenant::ShowYourself
*
* Purpose:
* Function that really just implements IOleClientSite::ShowObject.
* Here we first check if the tenant is fully visible, and if so, then
* nothing needs to happen. Otherwise, if the upper left corner of
* the tenant is in the upper left visible quadrant of the window, we'll
* also consider ourselves done. Otherwise we'll put the upper left
* corner of the object at the upper left corner of the window.
*
* Parameters:
* None
*
* Return Value:
* None
*/
void CTenant::ShowYourself(void)
{
RECTL rcl;
RECT rc;
POINT pt1, pt2;
//Scrolling deals in device units, so get our rectangle in those.
RectGet(&rcl, TRUE);
//Get the window rectangle offset for the current scroll position.
GetClientRect(m_hWnd, &rc);
OffsetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos);
//Check if the object is already visible. (macro in bookguid.h)
SETPOINT(pt1, (int)rcl.left, (int)rcl.top);
SETPOINT(pt2, (int)rcl.right, (int)rcl.bottom);
if (PtInRect(&rc, pt1) && PtInRect(&rc, pt2))
return;
//Check if the upper left is within the upper left quadrant
if (((int)rcl.left > rc.left && (int)rcl.left < ((rc.right+rc.left)/2))
&& ((int)rcl.top > rc.top && (int)rcl.top < ((rc.bottom+rc.top)/2)))
return;
//These are macros in INC\WIN1632.H
SendScrollPosition(m_hWnd, WM_HSCROLL, rcl.left-8);
SendScrollPosition(m_hWnd, WM_VSCROLL, rcl.top-8);
return;
}
/*
* CTenant::AddVerbMenu
*
* Purpose:
* Creates the variable verb menu item for the object in this tenant.
*
* Parmeters:
* hMenu HMENU on which to add items.
* iPos UINT position on that menu to add items.
*
* Return Value:
* None
*/
void CTenant::AddVerbMenu(HMENU hMenu, UINT iPos)
{
HMENU hMenuTemp;
LPOLEOBJECT pObj=m_pIOleObject;
//If we're static, say we have no object.
if (TENANTTYPE_STATIC==m_tType)
pObj=NULL;
OleUIAddVerbMenu(pObj, NULL, hMenu, iPos, IDM_VERBMIN
, FALSE, 0, &hMenuTemp);
return;
}
/*
* CTenant::CopyEmbeddedObject
*
* Purpose:
* Copies an embedded object to the given data object (via SetData, assuming
* this is a data transfer object for clipboard/drag-drop) if that's what
* we're holding.
*
* Parameters:
* pIDataObject LPDATAOBJECT in which to store the copy.
* pFE LPFORMATETC into which to copy CF_EMBEDDEDOBJECT
* if we put that in the data object.
* pptl LPPOINTL to the pick point (NULL outside of drag-drop);
*
* Return Value:
* None
*/
void CTenant::CopyEmbeddedObject(LPDATAOBJECT pIDataObject, LPFORMATETC pFE
, LPPOINTL pptl)
{
LPPERSISTSTORAGE pIPS;
STGMEDIUM stm;
FORMATETC fe;
HRESULT hr;
UINT cf;
POINTL ptl;
//Can only copy embeddings.
if (TENANTTYPE_EMBEDDEDOBJECT!=m_tType)
return;
if (NULL==pptl)
{
SETPOINTL(ptl, 0, 0);
pptl=&ptl;
}
/*
* Create CF_EMBEDDEDOBJECT. This is simply an IStorage with a
* copy of the embedded object in it. The not-so-simple part is
* getting an IStorage to stuff it in. For this operation we'll
* use a temporary compound file.
*/
stm.tymed=TYMED_ISTORAGE;
hr=StgCreateDocfile(NULL, STGM_TRANSACTED | STGM_READWRITE | STGM_CREATE
| STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &stm.pstg);
if (FAILED(hr))
return;
m_pObj->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPS);
if (NOERROR==pIPS->IsDirty())
{
OleSave(pIPS, stm.pstg, FALSE);
pIPS->SaveCompleted(NULL);
}
else
m_pIStorage->CopyTo(0, NULL, NULL, stm.pstg);
pIPS->Release();
//stm.pstg now has a copy, so stuff it away.
cf=RegisterClipboardFormat(CF_EMBEDDEDOBJECT);
SETDefFormatEtc(fe, cf, TYMED_ISTORAGE);
if (SUCCEEDED(pIDataObject->SetData(&fe, &stm, TRUE)))
*pFE=fe;
else
ReleaseStgMedium(&stm);
//Create CF_OBJECTDESCRIPTOR which OLE2UI handles.
stm.tymed=TYMED_HGLOBAL;
/*
* You want to make sure that if this object is iconic, that you
* create the object descriptor with DVASPECT_ICON instead of
* the more typical DVASPECT_CONTENT. Also remember that
* the pick point is in HIMETRIC.
*/
XformSizeInPixelsToHimetric(NULL, (LPSIZEL)pptl, (LPSIZEL)&ptl);
stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject(m_pIOleObject
, NULL, m_fe.dwAspect, ptl);
cf=RegisterClipboardFormat(CF_OBJECTDESCRIPTOR);
SETDefFormatEtc(fe, cf, TYMED_HGLOBAL);
if (FAILED(pIDataObject->SetData(&fe, &stm, TRUE)))
ReleaseStgMedium(&stm);
return;
}
/*
* CTenant::NotifyOfRename
*
* Purpose:
* Instructs the tenant that the document was saved under a different
* name. In order to keep the right compound document user interface,
* this tenant needs to tell its object through IOleObject::SetHostNames.
*
* Parameters:
* pszFile LPSTR of filename.
* pvReserved LPVOID reserved for future use.
*
* Return Value:
* None
*/
void CTenant::NotifyOfRename(LPSTR pszFile, LPVOID pvReserved)
{
char szObj[40];
char szApp[40];
if (NULL==m_pIOleObject)
return;
if (0==*pszFile)
LoadString(m_pPG->m_hInst, IDS_UNTITLED, szObj, sizeof(szObj));
else
{
GetFileTitle(pszFile, szObj, sizeof(szObj));
#ifndef WIN32
//Force filenames to uppercase in DOS versions.
AnsiUpper(szObj);
#endif
}
LoadString(m_pPG->m_hInst, IDS_CAPTION, szApp, sizeof(szApp));
m_pIOleObject->SetHostNames(szApp, szObj);
return;
}
//End CHAPTER9MOD
/*
* CTenant::Activate
*
* Purpose:
* Activates a verb on the object living in the tenant. Does nothing
* for static objects.
*
* Parameters:
* iVerb DWORD of the verb to execute.
*
* Return Value:
* BOOL TRUE if the object changed due to this verb execution.
*/
BOOL CTenant::Activate(DWORD iVerb)
{
//CHAPTER9MOD
RECT rc, rcH;
HCURSOR hCur;
//Can't activate statics.
if (TENANTTYPE_STATIC==m_tType)
{
MessageBeep(0);
return FALSE;
}
RECTFROMRECTL(rc, m_rcl);
RectConvertMappings(&rc, NULL, TRUE);
XformRectInPixelsToHimetric(NULL, &rc, &rcH);
hCur=SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT)));
ShowCursor(TRUE);
m_pIOleObject->DoVerb(iVerb, NULL, m_pIOleClientSite, 0, m_hWnd, &rcH);
SetCursor(hCur);
ShowCursor(FALSE);
//If the object does change, IAdviseSink::OnViewChange will see it.
return FALSE;
//End CHAPTER9MOD
}
/*
* CTenant::Draw
*
* Purpose:
* Draws the tenant in its rectangle on the given hDC. We assume the
* DC is already set up for the mapping mode in which our rectangle
* is expressed, since the Page we're in tells us both the rect and
* the hDC.
*
* Parameters:
* hDC HDC in which to draw. Could be a metafile, memory
* DC, screen, or printer. We simply don't know.
* ptd DVTARGETDEVICE FAR * describing the device.
* hIC HDC holding an information context (printing).
* xOff, yOff int offsets for the page in lometric
* fNoColor BOOL indicating if we should do B & W
* fPrinter BOOL indicating if we should render for a printer.
*
* Return Value:
* None
*/
void CTenant::Draw(HDC hDC, DVTARGETDEVICE FAR *ptd, HDC hIC, int xOff
, int yOff, BOOL fNoColor, BOOL fPrinter)
{
HRESULT hr;
RECT rc;
RECTL rcl;
UINT uMM;
//CHAPTER9MOD
//We hold IViewObject all the time now, so no need for QueryInterface
//End CHAPTER9MOD
RECTFROMRECTL(rc, m_rcl);
OffsetRect(&rc, -xOff, -yOff);
RECTLFROMRECT(rcl, rc);
//CHAPTER9MOD
//Repaint needs to erase the rectangle to insure full object cleanup
if (!fNoColor && !fPrinter)
{
COLORREF cr;
cr=SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
ExtTextOut(hDC, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL);
SetBkColor(hDC, cr);
}
//We have to use Draw since we have a target device and IC.
hr=m_pIViewObject->Draw(m_fe.dwAspect, -1, NULL, ptd, hIC, hDC
, &rcl, NULL, NULL, 0);
//End CHAPTER9MOD
/*
* If Draw failed, then perhaps it couldn't work for the device,
* so try good old OleDraw as a last resort. The code will
* generally be OLE_E_BLANK.
*/
if (FAILED(hr))
OleDraw(m_pObj, m_fe.dwAspect, hDC, &rc);
//CHAPTER9MOD
if (!fPrinter && (TENANTSTATE_SELECTED | TENANTSTATE_OPEN) & m_dwState)
{
/*
* Draw sizing handles to show the selection state. We convert
* things to MM_TEXT since that's what this function expects.
*/
RectConvertMappings(&rc, NULL, TRUE);
uMM=SetMapMode(hDC, MM_TEXT);
if (TENANTSTATE_SELECTED & m_dwState)
{
OleUIDrawHandles(&rc, hDC, OLEUI_HANDLES_INSIDE
| OLEUI_HANDLES_NOBORDER | OLEUI_HANDLES_USEINVERSE
, CXYHANDLE, TRUE);
}
if (TENANTSTATE_OPEN & m_dwState)
OleUIDrawShading(&rc, hDC, OLEUI_SHADE_FULLRECT, 0);
uMM=SetMapMode(hDC, uMM);
}
//End CHAPTER9MOD
return;
}
/*
* CTenant::Repaint
* CTenant::Invalidate
*
* Purpose:
* Repaints the tenant where it lies or invalidates its area
* for later repainting.
*
* Parameters:
* None
*
* Return Value:
* None
*/
void CTenant::Repaint(void)
{
RECT rc;
HDC hDC;
//CHAPTER9MOD
/*
* We might be asked to repaint from IOleClientSite::OnShowWindow
* after we've switched pages if our server was runnnig. This check
* on m_cOpens prevents that.
*/
if (0==m_cOpens)
return;
//End CHAPTER9MOD
hDC=GetDC(m_hWnd);
SetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos, 0, 0);
RectConvertMappings(&rc, NULL, FALSE);
SetMapMode(hDC, MM_LOMETRIC);
Draw(hDC, NULL, NULL, rc.left, rc.top, FALSE, FALSE);
ReleaseDC(m_hWnd, hDC);
return;
}
void CTenant::Invalidate(void)
{
RECTL rcl;
RECT rc;
RectGet(&rcl, TRUE);
RECTFROMRECTL(rc, rcl);
OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
InvalidateRect(m_hWnd, &rc, FALSE);
return;
}
/*
* CTenant::ObjectGet
*
* Purpose:
* Retrieves the LPUNKNOWN of the object in use by this tenant
*
* Parameters:
* ppUnk LPUNKNOWN FAR * in which to return the object pointer.
*
* Return Value:
* None
*/
void CTenant::ObjectGet(LPUNKNOWN FAR *ppUnk)
{
if (NULL!=ppUnk)
{
*ppUnk=m_pObj;
m_pObj->AddRef();
}
return;
}
/*
* CTenant::FormatEtcGet
*
* Purpose:
* Retrieves the FORMATETC in use by this tenant
*
* Parameters:
* pFE LPFORMATETC in which to store the information.
* fPresentation BOOL indicating if we want the real format or that
* of the presentation.
*
* Return Value:
* None
*/
void CTenant::FormatEtcGet(LPFORMATETC pFE, BOOL fPresentation)
{
if (NULL!=pFE)
{
*pFE=m_fe;
//CHAPTER9MOD
//If there is no format, use metafile (for embedded objects)
if (fPresentation || 0==pFE->cfFormat)
{
//Don't mess with dwAspect since it might be icon or content.
pFE->cfFormat=CF_METAFILEPICT;
pFE->tymed=TYMED_MFPICT;
}
//End CHAPTER9MOD
}
return;
}
/*
* CTenant::SizeGet
* CTenant::SizeSet
* CTenant::RectGet
* CTenant::RectSet
*
* Purpose:
* Returns or sets the size/position of the object contained here.
*
* Parameters:
* pszl/prcl LPSIZEL (Size) or LPRECTL (Rect) with the extents of
* interest. In Get situations, this will receive the
* extents; in Set it contains the extents.
* fDevice BOOL indicating that pszl/prcl is expressed in device
* units. Otherwise it's LOMETRIC.
*
* Return Value:
* None
*/
void CTenant::SizeGet(LPSIZEL pszl, BOOL fDevice)
{
if (!fDevice)
{
pszl->cx=m_rcl.right-m_rcl.left;
pszl->cy=m_rcl.bottom-m_rcl.top;
}
else
{
RECT rc;
SetRect(&rc, (int)(m_rcl.right-m_rcl.left)
, (int)(m_rcl.bottom-m_rcl.top), 0, 0);
RectConvertMappings(&rc, NULL, TRUE);
pszl->cx=(long)rc.left;
pszl->cy=(long)rc.top;
}
return;
}
void CTenant::SizeSet(LPSIZEL pszl, BOOL fDevice)
{
SIZEL szl;
if (!fDevice)
{
szl=*pszl;
m_rcl.right =pszl->cx+m_rcl.left;
m_rcl.bottom=pszl->cy+m_rcl.top;
}
else
{
RECT rc;
SetRect(&rc, (int)pszl->cx, (int)pszl->cy, 0, 0);
RectConvertMappings(&rc, NULL, FALSE);
m_rcl.right =(long)rc.left+m_rcl.left;
m_rcl.bottom=(long)rc.top+m_rcl.top;
SETSIZEL(szl, (long)rc.left, (long)rc.top);
}
//Tell OLE that this object was resized.
if (NULL!=m_pObj)
{
HRESULT hr;
LPOLEOBJECT pIOleObject;
hr=m_pObj->QueryInterface(IID_IOleObject, (LPVOID FAR *)&pIOleObject);
if (SUCCEEDED(hr))
{
//Convert our LOMETRIC into HIMETRIC by *=10
szl.cx*=10;
szl.cy*=-10; //Our size is stored negative.
pIOleObject->SetExtent(m_fe.dwAspect, &szl);
pIOleObject->Release();
}
}
return;
}
void CTenant::RectGet(LPRECTL prcl, BOOL fDevice)
{
if (!fDevice)
*prcl=m_rcl;
else
{
RECT rc;
RECTFROMRECTL(rc, m_rcl);
RectConvertMappings(&rc, NULL, TRUE);
RECTLFROMRECT(*prcl, rc);
}
return;
}
void CTenant::RectSet(LPRECTL prcl, BOOL fDevice)
{
SIZEL szl;
if (!fDevice)
m_rcl=*prcl;
else
{
RECT rc;
RECTFROMRECTL(rc, *prcl);
RectConvertMappings(&rc, NULL, FALSE);
RECTLFROMRECT(m_rcl, rc);
}
//Tell ourselves that the size changes.
SETSIZEL(szl, m_rcl.right-m_rcl.left, m_rcl.bottom-m_rcl.top);
SizeSet(&szl, FALSE);
return;
}
/*
* CTenant::CreateStatic
* (Protected)
*
* Purpose:
* Creates a new static bitmap or metafile object for this tenant
* using a freeloading method allowing us to specify exactly which
* type of data we want to paste since OleCreateStaticFromData doesn't.
*
* Parameters:
* pIDataObject LPDATAOBJECT from which to paste.
* pFE LPFORMATETC describing the format to paste.
* ppObj LPUNKNOWN FAR * into which we store the object pointer.
*
* Return Value:
* HRESULT NOERROR on success, error code otherwise.
*/
HRESULT CTenant::CreateStatic(LPDATAOBJECT pIDataObject, LPFORMATETC pFE
, LPUNKNOWN FAR * ppObj)
{
HRESULT hr;
STGMEDIUM stm;
LPUNKNOWN pIUnknown;
LPOLECACHE pIOleCache;
LPPERSISTSTORAGE pIPersistStorage;
CLSID clsID;
*ppObj=NULL;
//Try to get the data desired as specified in pFE->cfFormat
hr=pIDataObject->GetData(pFE, &stm);
if (FAILED(hr))
return hr;
//Create the object to handle this data.
if (CF_METAFILEPICT==pFE->cfFormat)
clsID=CLSID_FreeMetafile;
else
clsID=CLSID_FreeDib;
hr=OleCreateDefaultHandler(clsID, NULL, IID_IUnknown
, (LPVOID FAR *)&pIUnknown);
if (FAILED(hr))
{
ReleaseStgMedium(&stm);
return hr;
}
//Stuff the data into the object
pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
pIPersistStorage->InitNew(m_pIStorage);
//Now that we have the cache object, shove the data into it.
pIUnknown->QueryInterface(IID_IOleCache, (LPVOID FAR *)&pIOleCache);
pIOleCache->Cache(pFE, ADVF_PRIMEFIRST, NULL);
hr=pIOleCache->SetData(pFE, &stm, TRUE);
pIOleCache->Release();
//Insure there is a persistent copy on the disk; OleSave does it all for us
OleSave(pIPersistStorage, m_pIStorage, TRUE);
pIPersistStorage->Release();
//The cache owns this now.
ReleaseStgMedium(&stm);
if (FAILED(hr))
pIUnknown->Release();
else
*ppObj=pIUnknown;
return hr;
}